home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Tools - Objects / MacApp / MacApp 2.0 CD Release / MacApp 2.0 (Many Libraries) / Examples / Cards / UCards.inc1.p < prev    next >
Encoding:
Text File  |  1990-03-27  |  51.2 KB  |  1,816 lines  |  [TEXT/MPS ]

  1. {$P}
  2. {[a-,body+,h-,o=100,r+,rec+,t=4,u+,#+,j=20/57/1$,n+]}
  3. { UCards.inc1.p}
  4. { Copyright © 1986-1990 by Apple Computer, Inc.  All rights reserved. }
  5.  
  6. CONST
  7.     kStdBufSize         = 1024;
  8.     kAboutDoc            = 1201;                            { About box showing document info }
  9.     kIDAppBuzz            = 1000;                         { Our buzz string resource }
  10.     bzNoCardsThere        = 1;                            { No cards to be shown }
  11.     kStaggerAmount        = 16;                            { How much to stagger windows by }
  12.     kInitialCacheSize    = 7;                            { How many cards will we keep in the cache }
  13.     kCacheGrowthRate    = 2;                            { If we need to grow the cache, how large
  14.                                                           should we make it }
  15.     kReserveSpace        = 32000;                        { How much space must be reserved per
  16.                                                           document }
  17.  
  18. VAR
  19.     gStaggerCount:        INTEGER;                        { Used by SimpleStagger }
  20.     gCache:             TCardCache;                     { The global cache for the application's
  21.                                                           cards }
  22.  
  23. {--------------------------------------------------------------------------------------------------}
  24. {$S AWriteFile}
  25.  
  26. PROCEDURE CopyFilePiece(fromRefNum: INTEGER; toRefNum: INTEGER; amount: LongInt; bufSize: INTEGER);
  27.  
  28.     VAR
  29.         amtLeft:            LongInt;
  30.         count:                LongInt;
  31.         bufPtr:             Ptr;
  32.         fi:                 FailInfo;
  33.  
  34. {--------------------------------------------------------------------------------------------------}
  35.  
  36.     PROCEDURE HdlCopyErr(error: OSErr; message: LongInt);
  37.  
  38.         BEGIN
  39.         DisposPtr(bufPtr);
  40.         END;
  41.  
  42.     BEGIN
  43.     bufPtr := NewPtr(bufSize);
  44.     FailNil(bufPtr);
  45.     amtLeft := amount;
  46.  
  47.     CatchFailures(fi, HdlCopyErr);
  48.     WHILE amtLeft > 0 DO
  49.         BEGIN
  50.         count := Min(amtLeft, bufSize);
  51.         FailOSErr(FSRead(fromRefNum, count, bufPtr));
  52.         FailOSErr(FSWrite(toRefNum, count, bufPtr));
  53.         amtLeft := amtLeft - bufSize;
  54.         END;
  55.     DisposPtr(bufPtr);
  56.     Success(fi);
  57.     END;
  58.  
  59. {--------------------------------------------------------------------------------------------------}
  60. {$S AInit}
  61.  
  62. PROCEDURE TCardsApplication.ICardsApplication;
  63.  
  64.     BEGIN
  65.     { Do Misc initialization here... }
  66.     gStaggerCount := 0;
  67.     IApplication(kFileType);
  68.  
  69.     New(gCache);
  70.     FailNil(gCache);
  71.     gCache.ICardCache(kInitialCacheSize, kCacheGrowthRate);
  72.  
  73.     IF qDebug THEN
  74.         gCache.SetELTType('TCard');
  75.  
  76.     { Suppress dead-stripping for the following classes }
  77.     IF gDeadStripSuppression THEN
  78.         BEGIN
  79.         IF Member(TObject(NIL), TCardView) THEN ;
  80.         IF Member(TObject(NIL), TEmptyView) THEN ;
  81.         END;
  82.     END;
  83.  
  84. {--------------------------------------------------------------------------------------------------}
  85. {$S AOpen}
  86.  
  87. FUNCTION TCardsApplication.DoMakeDocument(itsCmdNumber: CmdNumber): TDocument; OVERRIDE;
  88.  
  89.     VAR
  90.         aCardDocument:        TCardDocument;
  91.  
  92.     BEGIN
  93.     New(aCardDocument);                                 { allocate the document }
  94.     FailNil(aCardDocument);                             { Make sure we could }
  95.  
  96.     aCardDocument.ICardDocument;                        { Initialize it }
  97.     DoMakeDocument := aCardDocument;                    { return the document object as the function
  98.                                                           value }
  99.     END;
  100.  
  101. {--------------------------------------------------------------------------------------------------}
  102. {$S AAboutApp}
  103.  
  104. PROCEDURE TCardsApplication.DoShowAboutApp; OVERRIDE;
  105.  { We override this so that the About… dialog will show information about
  106.    the currently open card document. }
  107.  
  108.     VAR
  109.         curWindow:            TWindow;
  110.         curCardDocument:    TCardDocument;
  111.         paramZero:            string[63];
  112.         paramOne:            Str255;
  113.         paramTwo:            string[2];
  114.         paramThree:            Str255;
  115.         dataBytes:            LongInt;
  116.         rsrcBytes:            LongInt;
  117.         theAlert:            INTEGER;
  118.  
  119.     BEGIN
  120.     curCardDocument := NIL;                                { Find the current document }
  121.     curWindow := GetFrontWindow;
  122.     IF curWindow <> NIL THEN                            { Make sure it's a Cards document }
  123.         IF Member(TObject(curWindow.fDocument), TCardDocument) THEN
  124.             curCardDocument := TCardDocument(curWindow.fDocument);
  125.  
  126.     IF curCardDocument = NIL THEN                        { there is no current document }
  127.         INHERITED DoShowAboutApp                        { …so show a generic About Box }
  128.     ELSE
  129.         BEGIN
  130.         theAlert := kAboutDoc;
  131.         paramZero := curCardDocument.fTitle^^;
  132.         NumToString(curCardDocument.fCards.GetSize, paramOne);
  133.         IF paramOne = '1' THEN
  134.             paramTwo := ''
  135.         ELSE
  136.             paramTwo := 's';
  137.         dataBytes := 0;
  138.         curCardDocument.DoNeedDiskSpace(dataBytes, rsrcBytes);
  139.         NumToString(dataBytes, paramThree);
  140.         ParamText(paramZero, paramOne, paramTwo, paramThree);
  141.         StdAlert(theAlert);                                    { display the about box }
  142.         END;
  143.  
  144.     END;
  145.  
  146. {--------------------------------------------------------------------------------------------------}
  147. {$S AOpen}
  148.  
  149. PROCEDURE TCardDocument.ICardDocument;
  150.  
  151.     BEGIN
  152.     { Set fCards and fCache to NIL in case initialization fails before they can be allocated. }
  153.     fCards := NIL;
  154.     fCache := NIL;
  155.  
  156.  { This is how we tell MacApp we're disk-based:  by indicating that we want
  157.    the data fork of the document left open. }
  158.     IDocument(kFileType, kSignature, kUsesDataFork, NOT kUsesRsrcFork, kDataOpen, NOT kRsrcOpen);
  159.  
  160.     fCardView := NIL;
  161.     fEmptyView := NIL;
  162.     fWorkRefNum := - 1;                                 { No work file, initially }
  163.     fWorkFileName := '';
  164.     fWorkVRefNum := - 1;
  165.     fCards := NewList;
  166.     IF qDebug THEN
  167.         fCards.SetELTType('TCard');
  168.     FailMemError;
  169.     fCache := gCache;                                    { What is the cache object for the
  170.                                                          application }
  171.  
  172.     CreateWorkFile;                                     { Now try creating a work file }
  173.     END;
  174.  
  175. {--------------------------------------------------------------------------------------------------}
  176. {$S AClose}
  177.  
  178. PROCEDURE TCardDocument.Free; OVERRIDE;
  179.  
  180.     BEGIN
  181.     FreeData;
  182.     fCache.FreeDocCards(SELF);
  183.     FreeIfObject(fCards);
  184.     { All views are now freed automatically }
  185.     PurgeWorkFile;
  186.     INHERITED Free;
  187.     END;
  188.  
  189. {--------------------------------------------------------------------------------------------------}
  190. {$S ADoCommand}
  191.  
  192. FUNCTION TCardDocument.AddCard: INTEGER;
  193.  
  194.     VAR
  195.         aCard:                TCard;
  196.         fi:                 FailInfo;
  197.  
  198. {--------------------------------------------------------------------------------------------------}
  199.  
  200.     PROCEDURE HdlInsertFailed(error: OSErr; message: LongInt);
  201.  
  202.         BEGIN
  203.         FreeIfObject(aCard);
  204.         END;
  205.  
  206.     BEGIN
  207.     New(aCard);
  208.     FailNil(aCard);
  209.     aCard.ICard(0, SELF);
  210.     CatchFailures(fi, HdlInsertFailed);
  211.     fCards.InsertLast(aCard);
  212.     Success(fi);
  213.     aCard.fLocked := TRUE;                                { Don't write out }
  214.     aCard.fDirty := TRUE;                                { Card is dirty (actually, non-existent) }
  215.     AddCard := fCards.GetSize;
  216.     END;
  217.  
  218. {--------------------------------------------------------------------------------------------------}
  219. {$S ARes}
  220.  
  221. PROCEDURE TCardDocument.CacheCard(aCard: TCard);
  222. { Force the specified card to be in the cache, so that its data handle is valid }
  223.  
  224.     BEGIN
  225.     fCache.CacheCard(SELF, aCard);
  226.     END;
  227.  
  228. {--------------------------------------------------------------------------------------------------}
  229. {$S AReadFile}
  230.  
  231. PROCEDURE TCardDocument.ConstructCardIndex(aRefNum: INTEGER);
  232.  { Build the in-memory index to the open card document.  Notice that the
  233.   card file architecture used here is rather inefficient, since the
  234.   lengths of the cards are scattered all over the disk.  In a real
  235.   application, you would almost certainly collect them in one place so
  236.   they could be read in a with a contiguous read.  We've only done it
  237.   this way so that we can use the same routines to write a card to the
  238.   work file and to the document file (i.e., laziness).    The main reason
  239.   the Resource Manager on the 64K ROM was so slow is that it used a
  240.   data structure similar to this. }
  241.  
  242.     VAR
  243.         nextFileLoc:        LongInt;                    { Next place in file }
  244.         cardChars:            INTEGER;                    { Size of a card }
  245.         nChars:             LongInt;                    { Count for I/O }
  246.         i:                    INTEGER;                    { Index for card being read }
  247.         aCard:                TCard;                        { A handy card }
  248.         fi:                 FailInfo;
  249.  
  250. {--------------------------------------------------------------------------------------------------}
  251.  
  252.     PROCEDURE HdlIndexFailure(error: OSErr; message: LongInt);
  253.  
  254.         BEGIN
  255.         FreeIfObject(aCard);
  256.         FreeData;                                        { Represent as an empty document }
  257.         END;
  258.  
  259.     BEGIN
  260.     aCard := NIL;
  261.     CatchFailures(fi, HdlIndexFailure);
  262.     FailOSErr(GetFPos(aRefNum, nextFileLoc));            { Get current position }
  263.  
  264.     FOR i := 1 TO fCardDocData.theCardCount DO
  265.         BEGIN
  266.         { Seek to the location of the next card's length, then read its length }
  267.         FailOSErr(SetFPos(aRefNum, fsFromStart, nextFileLoc));
  268.  
  269.         nChars := SIZEOF(cardChars);                    { Read the size }
  270.         FailOSErr(FSRead(aRefNum, nChars, @cardChars));
  271.  
  272.   { Create a new card object to represent the card, and insert it in our
  273.    index. Also calculate the location of the next card's size. }
  274.         New(aCard);
  275.         FailNil(aCard);
  276.         aCard.ICard(nextFileLoc, SELF);
  277.         aCard.fDataSize := cardChars;
  278.         fCards.InsertLast(aCard);
  279.         aCard := NIL;
  280.  
  281.         nextFileLoc := nextFileLoc + SIZEOF(INTEGER) + cardChars;
  282.         END;
  283.     Success(fi);
  284.     END;
  285.  
  286. {--------------------------------------------------------------------------------------------------}
  287. {$S AOpen}
  288.  
  289. PROCEDURE TCardDocument.CreateWorkFile;
  290.  
  291.     VAR
  292.         aFileName:            Str255;
  293.         aVRefNum:            INTEGER;
  294.         workRefNum:         INTEGER;
  295.  
  296.     BEGIN
  297.  { Use TDocument.GetTempName to get a unique name to avoid Switcher/shared
  298.    disk collisions. Put the file on the default volume (i.e., the one
  299.    the application was run from.  In the highly unlikely event we do get
  300.    a name conflict, don't trash whatever file we do conflict with. }
  301.  
  302.     GetTempName(aFileName);
  303.     aVRefNum := gConfiguration.sysVRefNum;
  304.     FailOSErr(Create(aFileName, aVRefNum, kSignature, kWorkType));
  305.     FailOSErr(FSOpen(aFileName, aVRefNum, workRefNum));
  306.  
  307.     fWorkVRefNum := aVRefNum;
  308.     fWorkFileName := aFileName;
  309.     fWorkRefNum := workRefNum;
  310.  
  311.     fWorkNext := 0;
  312.     END;
  313.  
  314. {--------------------------------------------------------------------------------------------------}
  315. {$S ARes}
  316.  
  317. PROCEDURE TCardDocument.DeleteCard(aCard: TCard);
  318.  
  319.     BEGIN
  320.     fCards.Delete(aCard);
  321.     fCache.Delete(aCard);
  322.     END;
  323.  
  324. {--------------------------------------------------------------------------------------------------}
  325. {$S AOpen}
  326.  
  327. PROCEDURE TCardDocument.DoInitialState; OVERRIDE;
  328.  
  329.     BEGIN
  330.     WITH fCardDocData DO
  331.         BEGIN
  332.         theShownCard := - 1;
  333.         theCardCount := 0;
  334.         END;
  335.     END;
  336.  
  337. {--------------------------------------------------------------------------------------------------}
  338. {$S AOpen}
  339.  
  340. PROCEDURE TCardDocument.DoMakeViews(forPrinting: BOOLEAN); OVERRIDE;
  341.  
  342.     VAR
  343.         aCardView:            TCardView;
  344.         aStdHandler:        TStdPrintHandler;
  345.         aWindow:            TWindow;
  346.  
  347.     BEGIN
  348.  { This document has two view types: one, a descendant of TTEView, shows
  349.    cards; the other displays a simple message when there are no cards to
  350.    show. We create the card view first. If we're not finder printing,
  351.    then we create the window with the empty view already installed.}
  352.  
  353.     aCardView := TCardView(DoCreateViews(SELF, NIL, kCardViewRsrcID, gZeroVPt));
  354.     FailNil(aCardView);                                 { Exit and tidy up if allocation fails }
  355.     fCardView := aCardView;
  356.  
  357.     New(aStdHandler);                                    { Make the card view printable }
  358.     FailNil(aStdHandler);
  359.     aStdHandler.IStdPrintHandler(SELF, fCardView, NOT kSquareDots, kFixedSize, kFixedSize);
  360.  
  361.     IF NOT forPrinting THEN
  362.         BEGIN
  363.         aWindow := NewTemplateWindow(kWindowRsrcID, SELF);
  364.         fEmptyView := TEmptyView(aWindow.FindSubView('EMPT'));
  365.  
  366.         New(aStdHandler);                                { Make the empty view printable }
  367.         FailNil(aStdHandler);
  368.         aStdHandler.IStdPrintHandler(SELF, fEmptyView, NOT kSquareDots, kFixedSize, kFixedSize);
  369.  
  370.   { Stick the appropriate view in the window, depending on whether there are
  371.     any cards or not. }
  372.         IF fCardDocData.theCardCount > 0 THEN
  373.             BEGIN
  374.             SwapViews(fEmptyView, fCardView);
  375.             fCardView.InstallCard(fCardDocData.theShownCard); { Restore the proper card }
  376.             END
  377.         ELSE
  378.             fCurrView := fEmptyView;
  379.         END
  380.  
  381.     ELSE                                                { Finder print only--don't need a window or
  382.                                                          an empty view }
  383.     IF fCardDocData.theCardCount <= 0 THEN
  384.         fDocPrintHandler := aStdHandler;
  385.     END;
  386.  
  387. {--------------------------------------------------------------------------------------------------}
  388. {$S ASelCommand}
  389.  
  390. FUNCTION TCardDocument.DoMenuCommand(aCmdNumber: CmdNumber): TCommand; OVERRIDE;
  391.  
  392.     VAR
  393.         aNewCardCommand:    TNewCardCommand;            { Command to add a card }
  394.  
  395.     BEGIN
  396.     { The only command the document handles is to create a new card. }
  397.     IF aCmdNumber = cNewCard THEN
  398.         BEGIN
  399.         New(aNewCardCommand);
  400.         FailNil(aNewCardCommand);
  401.         aNewCardCommand.INewCardCommand(aCmdNumber, SELF);
  402.         DoMenuCommand := aNewCardCommand;
  403.         END
  404.     ELSE
  405.         DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  406.     END;
  407.  
  408. {--------------------------------------------------------------------------------------------------}
  409. {$S AWriteFile}
  410.  
  411. PROCEDURE TCardDocument.DoNeedDiskSpace(VAR dataForkBytes, rsrcForkBytes: LongInt); OVERRIDE;
  412.  
  413.     VAR
  414.         aSize:                LongInt;
  415.  
  416. {--------------------------------------------------------------------------------------------------}
  417.  
  418.     PROCEDURE AccumulateSize(obj: TObject);
  419.  
  420.         BEGIN
  421.         IF NOT TCard(obj).fDeleted THEN
  422.             IF TCard(obj) = fCardView.fCurrCard THEN
  423.                 aSize := aSize + GetHandleSize(fCardView.fText) + SIZEOF(INTEGER)
  424.             ELSE
  425.                 aSize := aSize + TCard(obj).fDataSize + SIZEOF(INTEGER);
  426.         END;
  427.  
  428.     BEGIN
  429.     INHERITED DoNeedDiskSpace(dataForkBytes, rsrcForkBytes);
  430.  
  431.     aSize := 0;
  432.     fCards.Each(AccumulateSize);
  433.     dataForkBytes := dataForkBytes + SIZEOF(CardDocData) + aSize;
  434.     END;
  435.  
  436. {--------------------------------------------------------------------------------------------------}
  437. {$S AReadFile}
  438.  
  439. PROCEDURE TCardDocument.DoRead(aRefNum: INTEGER; rsrcExists, forPrinting: BOOLEAN); OVERRIDE;
  440.  
  441.     VAR
  442.         theCardDocData:     CardDocData;                { Temporary buffer }
  443.         nChars:             LongInt;                    { Count for I/O }
  444.         fi:                 FailInfo;
  445.  
  446. {--------------------------------------------------------------------------------------------------}
  447.  
  448.     PROCEDURE HdlReadFailed(error: OSErr; message: LongInt);
  449.  
  450.         BEGIN
  451.         DoInitialState;                                 { Get into self-consistent state for Free }
  452.         END;
  453.  
  454.     BEGIN
  455.     INHERITED DoRead(aRefNum, rsrcExists, forPrinting);
  456.     nChars := SIZEOF(CardDocData);                        { Read the size }
  457.     FailOSErr(FSRead(aRefNum, nChars, @theCardDocData));
  458.     fCardDocData := theCardDocData;
  459.     { When called on to read the document, just build the in-memory index. }
  460.     ConstructCardIndex(aRefNum);
  461.     END;
  462.  
  463. {--------------------------------------------------------------------------------------------------}
  464. {$S ARes}
  465.  
  466. PROCEDURE TCardDocument.DoSetupMenus; OVERRIDE;
  467.  
  468.     BEGIN
  469.     INHERITED DoSetupMenus;
  470.     Enable(cNewCard, TRUE);
  471.     END;
  472.  
  473. {--------------------------------------------------------------------------------------------------}
  474. {$S AWriteFile}
  475.  
  476. PROCEDURE TCardDocument.DoWrite(aRefNum: INTEGER; makingCopy: BOOLEAN); OVERRIDE;
  477.  { This procedure must merge the contents of the work file and the saved
  478.   document file (if any) into a new, clean copy of the document. }
  479.  
  480.     VAR
  481.         chunkSize:            LongInt;                    { Size of contiguous chunk we can copy }
  482.         theCardDocData:     CardDocData;                { File header information }
  483.         nextFileLoc:        LongInt;                    { Next expected file position }
  484.         nextOutLoc:         LongInt;                    { Next output location }
  485.  
  486. {--------------------------------------------------------------------------------------------------}
  487.  
  488.     PROCEDURE CopyFlush;
  489.     { If there are any cards left uncopied, copy them now. }
  490.  
  491.         BEGIN
  492.         IF chunkSize > 0 THEN
  493.             BEGIN
  494.             CopyFilePiece(fDataRefNum, aRefNum, chunkSize, kStdBufSize);
  495.             chunkSize := 0;
  496.             END;
  497.         END;
  498.  
  499. {--------------------------------------------------------------------------------------------------}
  500.  
  501.     PROCEDURE CopyToFile(obj: TObject);
  502.  
  503.     { Either write a card to the output file, or save it up in a batch for
  504.       copying en masse.  This scheme, although not terribly sophisticated,
  505.       is an example of how to special-case code to improve performance.
  506.       The idea is to avoid reading and writing the cards one at a time when
  507.       a contiguous batch of them from the original file have never been
  508.       changed. }
  509.  
  510.         VAR
  511.             aCard:                TCard;
  512.  
  513.         BEGIN
  514.         aCard := TCard(obj);
  515.  
  516.         WITH aCard DO
  517.             BEGIN
  518.             IF fChanged OR fDirty THEN
  519.     { The card does not match what's in the file, and so forces an
  520.       end to the previous chunk.  Write out what we have now,
  521.       and write the card out individually. }
  522.                 BEGIN
  523.                 CopyFlush;
  524.                 CacheCard(aCard);
  525.                 WriteCopy(aRefNum);
  526.                 END
  527.             ELSE
  528.                 BEGIN
  529.                 IF nextFileLoc <> fLocInFile THEN
  530.                 { End of previous contiguous chunk }
  531.                     CopyFlush;
  532.                 IF chunkSize <= 0 THEN                    { We're starting a new chunk }
  533.                     BEGIN
  534.                     FailOSErr(SetFPos(fDataRefNum, fsFromStart, fLocInFile));
  535.                     nextFileLoc := fLocInFile;
  536.                     END;
  537.                 chunkSize := chunkSize + SIZEOF(INTEGER) + fDataSize;
  538.                 nextFileLoc := nextFileLoc + SIZEOF(INTEGER) + fDataSize;
  539.                 END;
  540.             END;
  541.         END;
  542.  
  543.     BEGIN
  544.     INHERITED DoWrite(aRefNum, makingCopy);
  545.     chunkSize := SIZEOF(CardDocData);                    { Get the size }
  546.     theCardDocData := fCardDocData;
  547.     FailOSErr(FSWrite(aRefNum, chunkSize, @theCardDocData));
  548.     FailOSErr(GetFPos(aRefNum, nextOutLoc));
  549.     fFirstWritten := nextOutLoc;
  550.     nextFileLoc := 0;
  551.     chunkSize := 0;
  552.     fCards.Each(CopyToFile);
  553.     CopyFlush;
  554.     END;
  555.  
  556. {--------------------------------------------------------------------------------------------------}
  557. {$S AClose}
  558.  
  559. PROCEDURE TCardDocument.EmptyWorkFile;
  560. { Truncate the work file to make it empty }
  561.  
  562.     VAR
  563.         err:                OSErr;
  564.  
  565.     BEGIN
  566.     IF fWorkVRefNum <= 0 THEN
  567.         BEGIN
  568.         { If this doesn't work, don't blow user off }
  569.         err := SetEOF(fWorkRefNum, 0);
  570.  
  571.         fWorkNext := 0;
  572.         END;
  573.     END;
  574.  
  575. {--------------------------------------------------------------------------------------------------}
  576. {$S AFields}
  577.  
  578. PROCEDURE TCardDocument.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  579.                                                    fieldType: INTEGER)); OVERRIDE;
  580.  
  581.     BEGIN
  582.     DoToField('TCardDocument', NIL, bClass);
  583.     DoToField('fCardView', @fCardView, bObject);
  584.     DoToField('fEmptyView', @fEmptyView, bObject);
  585.     DoToField('fCurrView', @fCurrView, bObject);
  586.     DoToField('fCards', @fCards, bObject);
  587.     DoToField('fCache', @fCache, bObject);
  588.     DoToField('fCardDocData', NIL, bTitle);
  589.     DoToField('  theCardCount', @fCardDocData.theCardCount, bInteger);
  590.     DoToField('  theShownCard', @fCardDocData.theShownCard, bInteger);
  591.     DoToField('fReopening', @fReopening, bBoolean);
  592.     DoToField('fWorkFileName', @fWorkFileName, bString);
  593.     DoToField('fWorkVRefNum', @fWorkVRefNum, bInteger);
  594.     DoToField('fWorkRefNum', @fWorkRefNum, bInteger);
  595.     DoToField('fWorkNext', @fWorkNext, bLongInt);
  596.     DoToField('fFirstWritten', @fFirstWritten, bLongInt);
  597.     INHERITED Fields(DoToField);
  598.     END;
  599.  
  600. {--------------------------------------------------------------------------------------------------}
  601. {$S ASelCommand}
  602.  
  603. FUNCTION TCardDocument.FirstCard: INTEGER;
  604.  { Figure out the first card in the document, filtering out any possibly
  605.    deleted card. }
  606.  
  607.     VAR
  608.         maybeCard:            INTEGER;                    { Might be this one }
  609.  
  610.     BEGIN
  611.     maybeCard := 1;
  612.     IF TCard(fCards.At(maybeCard)).fDeleted THEN
  613.         maybeCard := NextCard(maybeCard);
  614.     FirstCard := maybeCard;
  615.     END;
  616.  
  617. {--------------------------------------------------------------------------------------------------}
  618. {$S AClose}
  619.  
  620. PROCEDURE TCardDocument.FreeData;
  621.  
  622.     BEGIN
  623.     PurgeCards;
  624.     EmptyWorkFile;
  625.     END;
  626.  
  627. {--------------------------------------------------------------------------------------------------}
  628. {$S ASelCommand}
  629.  
  630. FUNCTION TCardDocument.LastCard: INTEGER;
  631.  { Figure out the last card in the document, filtering out any possibly
  632.    deleted card. }
  633.  
  634.     VAR
  635.         maybeCard:            INTEGER;                    { Might be this one }
  636.  
  637.     BEGIN
  638.     maybeCard := fCards.GetSize;
  639.     IF TCard(fCards.At(maybeCard)).fDeleted THEN
  640.         maybeCard := PrevCard(maybeCard);
  641.     LastCard := maybeCard;
  642.     END;
  643.  
  644. {--------------------------------------------------------------------------------------------------}
  645. {$S ARes}
  646.  
  647. FUNCTION TCardDocument.NextCard(theCard: INTEGER): INTEGER;
  648.  { Figure out the next card in the document, filtering out any possibly
  649.    deleted card. }
  650.  
  651.     VAR
  652.         maybeCard:            INTEGER;                    { Might be this one }
  653.  
  654.     BEGIN
  655.     maybeCard := theCard;
  656.     REPEAT
  657.         maybeCard := maybeCard + 1;
  658.     UNTIL (maybeCard > fCards.GetSize) | (NOT TCard(fCards.At(maybeCard)).fDeleted);
  659.     IF maybeCard > fCards.GetSize THEN
  660.         maybeCard := - 1;
  661.     NextCard := maybeCard;
  662.     END;
  663.  
  664. {--------------------------------------------------------------------------------------------------}
  665. {$S ARes}
  666.  
  667. FUNCTION TCardDocument.PrevCard(theCard: INTEGER): INTEGER;
  668.  { Figure out the previous card in the document, filtering out any possibly
  669.    deleted card. }
  670.  
  671.     VAR
  672.         maybeCard:            INTEGER;                    { Might be this one }
  673.  
  674.     BEGIN
  675.     maybeCard := theCard;
  676.     REPEAT
  677.         maybeCard := maybeCard - 1;
  678.     UNTIL (maybeCard <= 0) | (NOT TCard(fCards.At(maybeCard)).fDeleted);
  679.     IF maybeCard <= 0 THEN
  680.         maybeCard := - 1;
  681.     PrevCard := maybeCard;
  682.     END;
  683.  
  684. {--------------------------------------------------------------------------------------------------}
  685. {$S AClose}
  686.  
  687. PROCEDURE TCardDocument.PurgeCards;
  688.  
  689.     BEGIN
  690.     IF fCache <> NIL THEN
  691.         fCache.FreeDocCards(SELF);
  692.  
  693.     IF fCards <> NIL THEN
  694.         BEGIN
  695.         fCards.Each(FreeIfObject);
  696.         fCards.DeleteAll;
  697.         END;
  698.     END;
  699.  
  700. {--------------------------------------------------------------------------------------------------}
  701. {$S AClose}
  702.  
  703. PROCEDURE TCardDocument.PurgeWorkFile;
  704. { Get rid of our working file }
  705.  
  706.     BEGIN
  707.     IF fWorkVRefNum <= 0 THEN
  708.         BEGIN
  709.         FailOSErr(FSClose(fWorkRefNum));
  710.         {$Push} {$H-}
  711.         FailOSErr(FSDelete(fWorkFileName, fWorkVRefNum));
  712.         {$Pop}
  713.         END;
  714.     fWorkVRefNum := 1;
  715.     fWorkFileName := '';
  716.     fWorkRefNum := - 1;
  717.     END;
  718.  
  719. {--------------------------------------------------------------------------------------------------}
  720. {$S ARes}
  721.  
  722. PROCEDURE TCardDocument.ReadCardFromDisk(aCard: TCard);
  723.  { Read a card from disk.  In the special case where we are being asked
  724.    to read a dirty card, it must be a newly created card which is not
  725.    yet in the cache, nor does it have a data handle.  Just create an
  726.    empty one for it. }
  727.  
  728.     VAR
  729.         aRefNum:            INTEGER;
  730.         aHandle:            Handle;
  731.  
  732.     BEGIN
  733.     IF aCard.fDirty THEN                                { Must be a new card, or we wouldn't be
  734.                                                          reading it }
  735.         BEGIN
  736.         aHandle := NewPermHandle(0);
  737.         aCard.fData := aHandle;
  738.         FailNil(aHandle);
  739.         END
  740.     ELSE
  741.         BEGIN
  742.         IF aCard.fChanged THEN
  743.             aRefNum := fWorkRefNum
  744.         ELSE
  745.             aRefNum := fDataRefNum;
  746.         aCard.ReadFrom(aRefNum);
  747.         END;
  748.     END;
  749.  
  750. {--------------------------------------------------------------------------------------------------}
  751. {$S AClose}
  752.  
  753. PROCEDURE TCardDocument.SavedOn(VAR fileName: Str255; volRefNum: INTEGER); OVERRIDE;
  754.  { Make changes to our in-memory data structures to reflect the fact that
  755.    we have successfully saved a clean copy of the document. These include
  756.    updating all the card's addresses and wiping the work file clean. }
  757.  
  758.     VAR
  759.         nextOutLoc:         LongInt;                    { Next position for a card }
  760.  
  761. {--------------------------------------------------------------------------------------------------}
  762.  
  763.     PROCEDURE LocateSelf(obj: TObject);
  764.  
  765.         VAR
  766.             aCard:                TCard;
  767.  
  768.         BEGIN
  769.         aCard := TCard(obj);
  770.         aCard.NewHome(nextOutLoc);
  771.         nextOutLoc := nextOutLoc + SIZEOF(INTEGER) + aCard.fDataSize;
  772.         END;
  773.  
  774.     BEGIN
  775.     INHERITED SavedOn(fileName, volRefNum);
  776.  
  777.  { Note that if this were an expensive operation, it might be put in DoWrite.
  778.    The DoWrite parameter makingCopy is intended to allow you to perform
  779.    updates such as this while you are in the process of writing the file
  780.    out. If FALSE, it indicates that you are writing the document itself
  781.    rather than a copy and thus you might want to update your in-memory
  782.    structures on the fly rather than after the fact, as we are doing here.
  783.    If you decide to do this, note that if the save fails for some reason
  784.    your in-memory structures will be left in an inconsistent state. }
  785.  
  786.     nextOutLoc := fFirstWritten;                        { First card written during last DoWrite }
  787.     fCards.Each(LocateSelf);
  788.     EmptyWorkFile;
  789.     END;
  790.  
  791. {--------------------------------------------------------------------------------------------------}
  792. {$S AReadFile}
  793.  
  794. PROCEDURE TCardDocument.ShowReverted; OVERRIDE;
  795.  { We must override this so that if the appropriate view to show changes
  796.    based on the contents of the reverted document, we install it properly. }
  797.  
  798.     BEGIN
  799.     fCardView.fCurrNumber := - 1;
  800.     fCardView.fCurrCard := NIL;
  801.     INHERITED ShowReverted;
  802.     IF fCardDocData.theCardCount = 0 THEN
  803.         BEGIN
  804.         IF fCurrView <> fEmptyView THEN
  805.             SwapViews(fCurrView, fEmptyView);
  806.         END
  807.     ELSE
  808.         BEGIN
  809.         IF fCurrView <> fCardView THEN
  810.             SwapViews(fCurrView, fCardView);
  811.         fCardView.InstallCard(fCardDocData.theShownCard);
  812.         gApplication.SetTarget(fCardView);
  813.         END;
  814.     END;
  815.  
  816. {--------------------------------------------------------------------------------------------------}
  817. {$S ARes}
  818.  
  819. PROCEDURE TCardDocument.SwapViews(fromView, toView: TView);
  820.  
  821.     VAR
  822.         itsScroller:        TScroller;
  823.         itsSize:            VPoint;
  824.  
  825.     BEGIN
  826.     itsScroller := fromView.GetScroller(FALSE);         { Get its scroller. }
  827.  
  828.     itsScroller.RemoveSubView(fromView);
  829.     itsScroller.AddSubView(toView);
  830.     itsSize := toView.fSize;
  831.     itsScroller.SetScrollLimits(itsSize,                { Make sure scroller gets adjusted. }
  832.                                 kVisible);
  833.     itsScroller.ForceRedraw;
  834.     fCurrView := toView;
  835.     toView.GetWindow.SetTarget(toView);                 { So toView becomes target when its window
  836.                                                          is active. }
  837.     END;
  838.  
  839. {--------------------------------------------------------------------------------------------------}
  840. {$S ARes}
  841.  
  842. PROCEDURE TCardDocument.WriteCardToDisk(aCard: TCard);
  843.  
  844.     BEGIN
  845.     IF aCard.fDirty THEN
  846.         BEGIN
  847.         aCard.Changed(fWorkNext);
  848.         fWorkNext := fWorkNext + aCard.fDataSize + SIZEOF(INTEGER);
  849.         aCard.WriteTo(fWorkRefNum);
  850.         END;
  851.     END;
  852.  
  853. {--------------------------------------------------------------------------------------------------}
  854. {$S AOpen}
  855.  
  856. PROCEDURE TCardView.IRes(itsDocument: TDocument; itsSuperView: TView; VAR itsParams: Ptr); OVERRIDE;
  857.  
  858.     BEGIN
  859.     INHERITED IRes(itsDocument, itsSuperView, itsParams);
  860.     fCardDocument := TCardDocument(itsDocument);
  861.     fCardEditCommand := NIL;
  862.     fCurrNumber := - 1;
  863.     fCurrCard := NIL;
  864.     END;
  865.  
  866. {--------------------------------------------------------------------------------------------------}
  867. {$S AClose}
  868.  
  869. PROCEDURE TCardView.Free; OVERRIDE;
  870.  
  871.     BEGIN
  872.     fText := DisposeIfHandle(fText);
  873.  
  874.     INHERITED Free;
  875.     END;
  876.  
  877. {--------------------------------------------------------------------------------------------------}
  878. {$S ASelCommand}
  879.  
  880. FUNCTION TCardView.DoKeyCommand(ch: Char; aKeyCode: INTEGER;
  881.     VAR info: EventInfo): TCommand; OVERRIDE;
  882.  { We must override this method so that we can encapsulate typing commands
  883.    from TEView within our own commands. This is so we can make sure
  884.    everything is set up properly before the encapsulated tries to redo or undo.
  885.    Another approach would have been to override TTEView.DoMakeTypingCommand
  886.    and create a descendant of TTETypingCommand which did the right things
  887.    and then called INHERITED Undo or Redo. This has the disadvantage that
  888.    we couldn't pass in extra parameters to ICardEditCommand.
  889.    
  890.    !!! NOTE: Future versions of MacApp will not return commands from DoKeyCommand, etc.
  891.    Instead, they will call PostCommand to "return" the command.  So… the second
  892.    solution is better for compatibility. }
  893.  
  894.     VAR
  895.         aCommand:            TCommand;                    { Command returned from TEView }
  896.         aCardEditCommand:    TCardEditCommand;            { Encapsulating command }
  897.  
  898.     BEGIN
  899.     aCommand := INHERITED DoKeyCommand(ch, aKeyCode, info);
  900.     IF aCommand = NIL THEN
  901.         DoKeyCommand := aCommand
  902.     ELSE
  903.         BEGIN
  904.         New(aCardEditCommand);
  905.         FailNil(aCardEditCommand);
  906.         aCardEditCommand.ICardEditCommand(cTyping, SELF, aCommand);
  907.         fCardEditCommand := aCardEditCommand;
  908.         DoKeyCommand := aCardEditCommand;
  909.         END;
  910.     END;
  911.  
  912. {--------------------------------------------------------------------------------------------------}
  913. {$S ASelCommand}
  914.  
  915. FUNCTION TCardView.DoMenuCommand(aCmdNumber: CmdNumber): TCommand; OVERRIDE;
  916.  { Handle our view's commands.  In the case of cut, copy, clear, and paste,
  917.   encapsulate the command objects returned from TTEView. See the
  918.   discussion in DoKeyCommand, above. }
  919.  
  920.     VAR
  921.         aCommand:            TCommand;                    { Command returned from TEView }
  922.         aCardEditCommand:    TCardEditCommand;            { Encapsulating command }
  923.         aNewCardCommand:    TNewCardCommand;            { Command to add a card }
  924.         aDeleteCardCommand: TDeleteCardCommand;         { Command to delete a card }
  925.  
  926.     BEGIN
  927.     DoMenuCommand := NIL;
  928.     CASE aCmdNumber OF
  929.         cNextCard:
  930.             InstallCard(fCardDocument.NextCard(fCurrNumber));
  931.         cPrevCard:
  932.             InstallCard(fCardDocument.PrevCard(fCurrNumber));
  933.         cFirstCard:
  934.             InstallCard(fCardDocument.FirstCard);
  935.         cLastCard:
  936.             InstallCard(fCardDocument.LastCard);
  937.         cDeleteCard:
  938.             BEGIN
  939.             New(aDeleteCardCommand);
  940.             FailNil(aDeleteCardCommand);
  941.             aDeleteCardCommand.IDeleteCardCommand(aCmdNumber, fCardDocument, fCurrCard,
  942.                                                   fCurrNumber);
  943.             DoMenuCommand := aDeleteCardCommand;
  944.             END;
  945.         cCut, cCopy, cPaste, cClear:
  946.             BEGIN
  947.             aCommand := INHERITED DoMenuCommand(aCmdNumber);
  948.             IF aCommand = NIL THEN
  949.                 DoMenuCommand := aCommand
  950.             ELSE
  951.                 BEGIN
  952.                 New(aCardEditCommand);
  953.                 FailNil(aCardEditCommand);
  954.                 aCardEditCommand.ICardEditCommand(aCmdNumber, SELF, aCommand);
  955.                 fCardEditCommand := aCardEditCommand;
  956.                 DoMenuCommand := aCardEditCommand;
  957.                 END;
  958.             END;
  959.         OTHERWISE
  960.             DoMenuCommand := INHERITED DoMenuCommand(aCmdNumber);
  961.     END;
  962.     END;
  963.  
  964. {--------------------------------------------------------------------------------------------------}
  965. {$S ARes}
  966.  
  967. PROCEDURE TCardView.DoSetupMenus; OVERRIDE;
  968.  
  969.     BEGIN
  970.     INHERITED DoSetupMenus;
  971.     IF fCurrNumber > 0 THEN
  972.         BEGIN
  973.         Enable(cFirstCard, TRUE);
  974.         Enable(cLastCard, TRUE);
  975.         Enable(cDeleteCard, TRUE);
  976.         Enable(cPrevCard, fCardDocument.PrevCard(fCurrNumber) > 0);
  977.         Enable(cNextCard, fCardDocument.NextCard(fCurrNumber) > 0);
  978.         END;
  979.     END;
  980.  
  981. {--------------------------------------------------------------------------------------------------}
  982. {$S AFields}
  983.  
  984. PROCEDURE TCardView.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  985.                                                fieldType: INTEGER)); OVERRIDE;
  986.  
  987.     BEGIN
  988.     DoToField('TCardView', NIL, bClass);
  989.     DoToField('fCardDocument', @fCardDocument, bObject);
  990.     DoToField('fCurrCard', @fCurrCard, bObject);
  991.     DoToField('fCurrNumber', @fCurrNumber, bInteger);
  992.     DoToField('fCardEditCommand', @fCardEditCommand, bObject);
  993.     INHERITED Fields(DoToField);
  994.     END;
  995.  
  996. {--------------------------------------------------------------------------------------------------}
  997. {$S ARes}
  998.  
  999. PROCEDURE TCardView.InstallCard(theCard: INTEGER);
  1000.  { Make the given card the currently displayed card in our TEView.    Update
  1001.   the currently displayed card if need be. }
  1002.  
  1003.     VAR
  1004.         aCard:                TCard;                        { Card we're trying to look up }
  1005.         newText:            Handle;
  1006.  
  1007.     BEGIN
  1008.     IF theCard = fCurrNumber THEN
  1009.         BEGIN
  1010.         {$IFC qDebug}
  1011.         WRITELN('Installing same card');
  1012.         {$ENDC}
  1013.         EXIT(InstallCard);
  1014.         END;
  1015.  
  1016.     IF (fCurrNumber >= 0) AND (fCurrCard <> NIL) THEN
  1017.   { Already a card showing; if any changes, update its text. The test
  1018.    for fCurrCard <> NIL should be unnecessary, but we do it just
  1019.    in case. }
  1020.         BEGIN
  1021.         DoneTyping;                                     { Tell TEView no more typing for current
  1022.                                                          command }
  1023.         UpdateCard(fCurrCard);                            { Copy text to current card }
  1024.         fCardEditCommand := NIL;                        { Cut command loose for now }
  1025.         END;
  1026.  
  1027.     fCurrNumber := theCard;
  1028.     fCardDocument.fCardDocData.theShownCard := theCard;
  1029.  
  1030.     IF theCard > 0 THEN
  1031.         BEGIN
  1032.         aCard := TCard(fCardDocument.fCards.At(theCard));
  1033.         {$IFC qDebug}
  1034.         IF aCard = NIL THEN
  1035.             BEGIN
  1036.             ProgramBreak('In TCardView.InstallCard, no card at requested index!');
  1037.             EXIT(InstallCard);
  1038.             END;
  1039.         {$ENDC}
  1040.         fCurrCard := aCard;
  1041.         fCardDocument.CacheCard(aCard);                 { Make sure data is around }
  1042.  
  1043.   { Ideally, if we couldn't get the text we should display something like
  1044.    "This card couldn't be read from disk", similar to MacWrite.  This
  1045.    would be easier if we were using one view; we could have a flag
  1046.    associated with the current card which set the view to display the
  1047.    message rather than the (non-existent) contents.  For this example,
  1048.    we don't bother and just display a blank card. }
  1049.         IF aCard.fData <> NIL THEN
  1050.             BEGIN
  1051.             newText := aCard.fData;
  1052.             FailOSErr(HandToHand(newText));
  1053.             END
  1054.         ELSE
  1055.             BEGIN
  1056.             newText := NewPermHandle(0);
  1057.             FailNil(newText);
  1058.             END;
  1059.         InstallText(newText);                            { make the view contain the new text }
  1060.         END;
  1061.     END;
  1062.  
  1063. {--------------------------------------------------------------------------------------------------}
  1064. {$S ARes}
  1065.  
  1066. PROCEDURE TCardView.InstallText(newText: Handle);
  1067.  
  1068.     BEGIN
  1069.     StuffText(newText);
  1070.     RecalcText;
  1071.     fLastHeight := 0;                                    { force AdjustSize in SynchView }
  1072.     SetSelect(0, 0, fHTE);                                { set selection point to start of text }
  1073.     SynchView(kRedraw);
  1074.     ForceRedraw;                                        { force the view to be redrawn }
  1075.     END;
  1076.  
  1077. {--------------------------------------------------------------------------------------------------}
  1078. {$S AReadFile}
  1079.  
  1080. PROCEDURE TCardView.ShowReverted; OVERRIDE;
  1081.  { When reverting, make sure the view displays the current card at the time
  1082.   of the last save. }
  1083.  
  1084.     BEGIN
  1085.     fCurrNumber := - 1;
  1086.     fCurrCard := NIL;
  1087.     InstallCard(fCardDocument.fCardDocData.theShownCard);
  1088.     INHERITED ShowReverted;
  1089.     END;
  1090.  
  1091. {--------------------------------------------------------------------------------------------------}
  1092. {$S ARes}
  1093.  
  1094. PROCEDURE TCardView.UpdateCard(theCard: TCard);
  1095.  
  1096.     BEGIN
  1097.     theCard.newText(fText);
  1098.     END;
  1099.  
  1100. {--------------------------------------------------------------------------------------------------}
  1101. {$S ARes}
  1102.  
  1103. PROCEDURE TEmptyView.Resize(width, height: VCoordinate; invalidate: BOOLEAN); OVERRIDE;
  1104. { Force a redraw since the text is fit to the box }
  1105.     BEGIN
  1106.     INHERITED Resize(width, height, invalidate);
  1107.     ForceRedraw;
  1108.     END;
  1109.  
  1110. {--------------------------------------------------------------------------------------------------}
  1111. {$S ARes}
  1112.  
  1113. PROCEDURE TEmptyView.Draw(area: Rect); OVERRIDE;
  1114. { Just tell the good folks there's nothing to be drawn. }
  1115.  
  1116.     VAR
  1117.         nobodyHome:         Str255;                     { How we tell folks there's nothing there. }
  1118.         qdExtent:            Rect;
  1119.  
  1120.     BEGIN
  1121.     GetIndString(nobodyHome, kIDAppBuzz, bzNoCardsThere);
  1122.     GetQDExtent(qdExtent);
  1123.     SetPortTextStyle(gSystemStyle);
  1124.     MADrawString(@nobodyHome, qdExtent, teJustSystem);
  1125.  
  1126.     INHERITED Draw(area);
  1127.     END;
  1128.  
  1129. {--------------------------------------------------------------------------------------------------}
  1130. {$S ARes}
  1131.  
  1132. PROCEDURE TCacheableObject.ICacheableObject;
  1133.  
  1134.     BEGIN
  1135.     fGeneration := 0;
  1136.     fLocked := FALSE;
  1137.     END;
  1138.  
  1139. {--------------------------------------------------------------------------------------------------}
  1140. {$S AFields}
  1141.  
  1142. PROCEDURE TCacheableObject.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1143.                                                       fieldType: INTEGER)); OVERRIDE;
  1144.  
  1145.     BEGIN
  1146.     DoToField('TCacheableObject', NIL, bClass);
  1147.     DoToField('fGeneration', @fGeneration, bInteger);
  1148.     DoToField('fLocked', @fLocked, bBoolean);
  1149.     INHERITED Fields(DoToField);
  1150.     END;
  1151.  
  1152. {--------------------------------------------------------------------------------------------------}
  1153. {$S ARes}
  1154.  
  1155. PROCEDURE TCard.ICard(itsFileLocation: LongInt; itsDocument: TCardDocument);
  1156.  
  1157.     BEGIN
  1158.     ICacheableObject;
  1159.     fData := NIL;
  1160.     fDataSize := 0;
  1161.     fDeleted := FALSE;
  1162.     fCardDocument := itsDocument;
  1163.     NewHome(itsFileLocation);
  1164.     END;
  1165.  
  1166. {--------------------------------------------------------------------------------------------------}
  1167. {$S ARes}
  1168.  
  1169. PROCEDURE TCard.Free; OVERRIDE;
  1170.  
  1171.     BEGIN
  1172.     fData := DisposeIfHandle(fData);
  1173.  
  1174.     INHERITED Free;
  1175.     END;
  1176.  
  1177. {--------------------------------------------------------------------------------------------------}
  1178. {$S ARes}
  1179.  
  1180. PROCEDURE TCard.Changed(itsFileLocation: LongInt);
  1181.  
  1182.     BEGIN
  1183.     fChanged := TRUE;
  1184.     fLocInFile := itsFileLocation;
  1185.     END;
  1186.  
  1187. {--------------------------------------------------------------------------------------------------}
  1188. {$S AFields}
  1189.  
  1190. PROCEDURE TCard.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1191.     fieldType: INTEGER)); OVERRIDE;
  1192.  
  1193.     BEGIN
  1194.     DoToField('TCard', NIL, bClass);
  1195.     DoToField('fData', @fData, bHandle);
  1196.     DoToField('fDataSize', @fDataSize, bInteger);
  1197.     DoToField('fDirty', @fDirty, bBoolean);
  1198.     DoToField('fChanged', @fChanged, bBoolean);
  1199.     DoToField('fDeleted', @fDeleted, bBoolean);
  1200.     DoToField('fLocInFile', @fLocInFile, bLongInt);
  1201.     DoToField('fCardDocument', @fCardDocument, bObject);
  1202.     INHERITED Fields(DoToField);
  1203.     END;
  1204.  
  1205. {--------------------------------------------------------------------------------------------------}
  1206. {$S ARes}
  1207.  
  1208. PROCEDURE TCard.NewHome(itsFileLocation: LongInt);
  1209.  
  1210.     BEGIN
  1211.     fChanged := FALSE;
  1212.     fDirty := FALSE;
  1213.     fLocInFile := itsFileLocation;
  1214.     END;
  1215.  
  1216. {--------------------------------------------------------------------------------------------------}
  1217. {$S ADoCommand}
  1218.  
  1219. PROCEDURE TCard.newText(aHandle: Handle);
  1220.  
  1221.     BEGIN
  1222.     fDirty := TRUE;
  1223.  
  1224.     fData := DisposeIfHandle(fData);
  1225.  
  1226.     fDataSize := GetHandleSize(aHandle);
  1227.     fData := NewPermHandle(fDataSize);
  1228.     FailNil(fData);
  1229.     BlockMove(aHandle^, fData^, fDataSize);
  1230.     END;
  1231.  
  1232. {--------------------------------------------------------------------------------------------------}
  1233. {$S ARes}
  1234.  
  1235. PROCEDURE TCard.ReadFrom(aRefNum: INTEGER);
  1236.  
  1237.     VAR
  1238.         nChars:             LongInt;
  1239.         itsData:            Handle;
  1240.  
  1241.     BEGIN
  1242.     FailOSErr(SetFPos(aRefNum, fsFromStart, fLocInFile + SIZEOF(INTEGER)));
  1243.  
  1244.     nChars := fDataSize;                                { Set the size }
  1245.  
  1246.     IF fData = NIL THEN
  1247.         BEGIN
  1248.         itsData := NewPermHandle(nChars);
  1249.         fData := itsData;
  1250.         END
  1251.     ELSE
  1252.         SetHandleSize(fData, nChars);
  1253.     FailOSErr(MemError);
  1254.  
  1255.     FailOSErr(FSRead(aRefNum, nChars, fData^));
  1256.  
  1257.     fDirty := FALSE;
  1258.     END;
  1259.  
  1260. {--------------------------------------------------------------------------------------------------}
  1261. {$S ARes}
  1262.  
  1263. PROCEDURE TCard.WriteTo(aRefNum: INTEGER);
  1264.  
  1265.     BEGIN
  1266.     IF fDirty THEN
  1267.         BEGIN
  1268.         FailOSErr(SetFPos(aRefNum, fsFromStart, fLocInFile));
  1269.         WriteCopy(aRefNum);
  1270.  
  1271.         fDirty := FALSE;
  1272.         END;
  1273.     END;
  1274.  
  1275. {--------------------------------------------------------------------------------------------------}
  1276. {$S ARes}
  1277.  
  1278. PROCEDURE TCard.WriteCopy(aRefNum: INTEGER);
  1279.  
  1280.     VAR
  1281.         nChars:             LongInt;
  1282.         cardChars:            INTEGER;
  1283.  
  1284.     BEGIN
  1285.     nChars := SIZEOF(INTEGER);                            { Write the size }
  1286.     cardChars := GetHandleSize(fData);
  1287.     FailOSErr(FSWrite(aRefNum, nChars, @cardChars));
  1288.  
  1289.     nChars := cardChars;                                { Write the data }
  1290.     FailOSErr(FSWrite(aRefNum, nChars, fData^));
  1291.     END;
  1292.  
  1293. {--------------------------------------------------------------------------------------------------}
  1294. {$S ASelCommand}
  1295.  
  1296. PROCEDURE TCardEditCommand.ICardEditCommand(itsCmdNumber: CmdNumber; itsView: TCardView;
  1297.                                             itsEncapsulatedCommand: TCommand);
  1298.  
  1299.     VAR
  1300.         fi:                 FailInfo;
  1301.  
  1302. {--------------------------------------------------------------------------------------------------}
  1303.  
  1304.     PROCEDURE HdlICardEditCommand(error: OSErr; message: LongInt);
  1305.  
  1306.         BEGIN
  1307.         Free;
  1308.         END;
  1309.  
  1310.     BEGIN
  1311.     fReserve := NIL;
  1312.     ICommand(itsCmdNumber, itsView.fDocument, NIL, NIL);
  1313.     fCardView := itsView;
  1314.     fEncapsulatedCommand := itsEncapsulatedCommand;
  1315.     fChangesClipboard := itsEncapsulatedCommand.fChangesClipboard;
  1316.     fCausesChange := itsEncapsulatedCommand.fCausesChange;
  1317.     fCanUndo := itsEncapsulatedCommand.fCanUndo;
  1318.     {$IFC qDebug}
  1319.     IF itsView.fCurrNumber < 0 THEN
  1320.         ProgramBreak('In TCardEditCommand.ICardEditCommand, no current card.');
  1321.     {$ENDC}
  1322.     fCardNumber := itsView.fCurrNumber;
  1323.     fCard := itsView.fCurrCard;
  1324.  
  1325.     CatchFailures(fi, HdlICardEditCommand);
  1326.     fReserve := NewPermHandle(kReserveSpace);
  1327.     IF fReserve = NIL THEN                                { couldn't get the space }
  1328.         BEGIN
  1329.         gApplication.CommitLastCommand;                    { free up space from previous command }
  1330.         fReserve := NewPermHandle(kReserveSpace);        { try once more }
  1331.         FailNil(fReserve);                                { if it still didn't work, give up }
  1332.         END;
  1333.     Success(fi);
  1334.     END;
  1335.  
  1336. {--------------------------------------------------------------------------------------------------}
  1337. {$S ADoCommand}
  1338.  
  1339. PROCEDURE TCardEditCommand.Free; OVERRIDE;
  1340.  
  1341.     BEGIN
  1342.     IF fCardView.fCardEditCommand = SELF THEN
  1343.         fCardView.fCardEditCommand := NIL;
  1344.  
  1345.     FreeIfObject(fEncapsulatedCommand);
  1346.     fEncapsulatedCommand := NIL;
  1347.  
  1348.     fReserve := DisposeIfHandle(fReserve);
  1349.  
  1350.     INHERITED Free;
  1351.     END;
  1352.  
  1353. {--------------------------------------------------------------------------------------------------}
  1354. {$S ADoCommand}
  1355.  
  1356. PROCEDURE TCardEditCommand.DoIt; OVERRIDE;
  1357. { Make sure we know the card has been changed, and that it can't be purged from the cache. }
  1358.  
  1359.     BEGIN
  1360.     WITH fCard DO
  1361.         BEGIN
  1362.         fDirty := TRUE;
  1363.         fLocked := TRUE;
  1364.         END;
  1365.     fEncapsulatedCommand.DoIt;
  1366.     END;
  1367.  
  1368. {--------------------------------------------------------------------------------------------------}
  1369. {$S ADoCommand}
  1370.  
  1371. PROCEDURE TCardEditCommand.UndoIt; OVERRIDE;
  1372.  
  1373.     BEGIN
  1374.     IF fCardView.fCardEditCommand <> SELF THEN
  1375.         BEGIN
  1376.         fCardView.InstallCard(fCardNumber);
  1377.         fCardView.fCardEditCommand := SELF;
  1378.         END;
  1379.     fEncapsulatedCommand.UndoIt;
  1380.     END;
  1381.  
  1382. {--------------------------------------------------------------------------------------------------}
  1383. {$S ADoCommand}
  1384.  
  1385. PROCEDURE TCardEditCommand.RedoIt; OVERRIDE;
  1386.  
  1387.     BEGIN
  1388.     IF fCardView.fCardEditCommand <> SELF THEN
  1389.         BEGIN
  1390.         fCardView.InstallCard(fCardNumber);
  1391.         fCardView.fCardEditCommand := SELF;
  1392.         END;
  1393.     fEncapsulatedCommand.RedoIt;
  1394.     END;
  1395.  
  1396. {--------------------------------------------------------------------------------------------------}
  1397. {$S ADoCommand}
  1398.  
  1399. PROCEDURE TCardEditCommand.Commit; OVERRIDE;
  1400.  
  1401.     BEGIN
  1402.     fReserve := DisposeIfHandle(fReserve);
  1403.  
  1404.     fCard.fLocked := FALSE;
  1405.     {$IFC FALSE}
  1406.     fCardView.DoneTyping;
  1407.     {$ENDC}
  1408.     IF fCardView.fCardEditCommand = SELF THEN
  1409.         fCardView.UpdateCard(fCard);
  1410.     fEncapsulatedCommand.Commit;
  1411.     END;
  1412.  
  1413. {--------------------------------------------------------------------------------------------------}
  1414. {$S AFields}
  1415.  
  1416. PROCEDURE TCardEditCommand.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1417.                                                       fieldType: INTEGER)); OVERRIDE;
  1418.  
  1419.     BEGIN
  1420.     DoToField('TCardEditCommand', NIL, bClass);
  1421.     DoToField('fEncapsulatedCommand', @fEncapsulatedCommand, bObject);
  1422.     DoToField('fCardView', @fCardView, bObject);
  1423.     DoToField('fCardNumber', @fCardNumber, bInteger);
  1424.     DoToField('fCard', @fCard, bObject);
  1425.     DoToField('fReserve', @fReserve, bHandle);
  1426.     INHERITED Fields(DoToField);
  1427.     END;
  1428.  
  1429. {--------------------------------------------------------------------------------------------------}
  1430. {$S ASelCommand}
  1431.  
  1432. PROCEDURE TNewCardCommand.INewCardCommand(itsCmdNumber: CmdNumber; itsCardDocument: TCardDocument);
  1433.  
  1434.     BEGIN
  1435.     ICommand(itsCmdNumber, itsCardDocument, NIL, NIL);
  1436.     fCardDocument := itsCardDocument;
  1437.     END;
  1438.  
  1439. {--------------------------------------------------------------------------------------------------}
  1440. {$S ADoCommand}
  1441.  
  1442. PROCEDURE TNewCardCommand.DoIt; OVERRIDE;
  1443.  
  1444.     BEGIN
  1445.     fCardNumber := fCardDocument.AddCard;
  1446.     WITH fCardDocument DO
  1447.         BEGIN
  1448.         fCard := TCard(fCards.At(fCardNumber));
  1449.         SELF.fSavedSelection := fCardView.fCurrNumber;
  1450.         IF fCardDocData.theCardCount <= 0 THEN
  1451.             SwapViews(fEmptyView, fCardView);
  1452.         fCardView.InstallCard(fCardNumber);
  1453.         END;
  1454.     END;
  1455.  
  1456. {--------------------------------------------------------------------------------------------------}
  1457. {$S ADoCommand}
  1458.  
  1459. PROCEDURE TNewCardCommand.UndoIt; OVERRIDE;
  1460.  
  1461.     BEGIN
  1462.     WITH fCardDocument DO
  1463.         BEGIN
  1464.         fCardView.InstallCard(fSavedSelection);
  1465.         DeleteCard(SELF.fCard);
  1466.         IF fCardDocData.theCardCount <= 0 THEN
  1467.             SwapViews(fCardView, fEmptyView);
  1468.         END;
  1469.     FreeIfObject(fCard);
  1470.     END;
  1471.  
  1472. {--------------------------------------------------------------------------------------------------}
  1473. {$S ADoCommand}
  1474.  
  1475. PROCEDURE TNewCardCommand.RedoIt; OVERRIDE;
  1476.  
  1477.     BEGIN
  1478.     DoIt;
  1479.     END;
  1480.  
  1481. {--------------------------------------------------------------------------------------------------}
  1482.  
  1483. {$S ADoCommand}
  1484.  
  1485. PROCEDURE TNewCardCommand.Commit; OVERRIDE;
  1486.  
  1487.     BEGIN
  1488.     fCard.fLocked := FALSE;                             { OK to page out now }
  1489.     fCardDocument.fCardDocData.theCardCount := fCardDocument.fCards.GetSize;
  1490.     END;
  1491.  
  1492. {--------------------------------------------------------------------------------------------------}
  1493. {$S AFields}
  1494.  
  1495. PROCEDURE TNewCardCommand.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1496.                                                      fieldType: INTEGER)); OVERRIDE;
  1497.  
  1498.     BEGIN
  1499.     DoToField('TNewCardCommand', NIL, bClass);
  1500.     DoToField('fCardDocument', @fCardDocument, bObject);
  1501.     DoToField('fCardNumber', @fCardNumber, bInteger);
  1502.     DoToField('fCard', @fCard, bObject);
  1503.     DoToField('fSavedSelection', @fSavedSelection, bInteger);
  1504.     INHERITED Fields(DoToField);
  1505.     END;
  1506.  
  1507. {--------------------------------------------------------------------------------------------------}
  1508. {$S ASelCommand}
  1509.  
  1510. PROCEDURE TDeleteCardCommand.IDeleteCardCommand(itsCmdNumber: CmdNumber;
  1511.                                                 itsCardDocument: TCardDocument; itsCard: TCard;
  1512.                                                 itsNumber: INTEGER);
  1513.  
  1514.     BEGIN
  1515.     ICommand(itsCmdNumber, itsCardDocument, NIL, NIL);
  1516.     fCardDocument := itsCardDocument;
  1517.     fCard := itsCard;
  1518.     fCardNumber := itsNumber;
  1519.     END;
  1520.  
  1521. {--------------------------------------------------------------------------------------------------}
  1522. {$S ADoCommand}
  1523.  
  1524. PROCEDURE TDeleteCardCommand.DoIt; OVERRIDE;
  1525.  
  1526.     VAR
  1527.         theCardView:        TCardView;
  1528.         aCardNumber:        INTEGER;
  1529.  
  1530.     BEGIN
  1531.     fCard.fDeleted := TRUE;
  1532.     theCardView := fCardDocument.fCardView;
  1533.     aCardNumber := fCardDocument.NextCard(fCardNumber);
  1534.     IF aCardNumber <= 0 THEN
  1535.         aCardNumber := fCardDocument.PrevCard(fCardNumber);
  1536.     theCardView.InstallCard(aCardNumber);
  1537.     IF aCardNumber <= 0 THEN
  1538.         fCardDocument.SwapViews(theCardView, fCardDocument.fEmptyView);
  1539.     END;
  1540.  
  1541. {--------------------------------------------------------------------------------------------------}
  1542. {$S ADoCommand}
  1543.  
  1544. PROCEDURE TDeleteCardCommand.UndoIt; OVERRIDE;
  1545.  
  1546.     BEGIN
  1547.     fCard.fDeleted := FALSE;
  1548.     WITH fCardDocument DO
  1549.         BEGIN
  1550.         IF fCardDocData.theCardCount <= 1 THEN
  1551.             SwapViews(fEmptyView, fCardView);
  1552.         fCardView.InstallCard(SELF.fCardNumber);
  1553.         END;
  1554.     END;
  1555.  
  1556. {--------------------------------------------------------------------------------------------------}
  1557. {$S ADoCommand}
  1558.  
  1559. PROCEDURE TDeleteCardCommand.RedoIt; OVERRIDE;
  1560.  
  1561.     BEGIN
  1562.     DoIt;
  1563.     END;
  1564.  
  1565. {--------------------------------------------------------------------------------------------------}
  1566. {$S ADoCommand}
  1567.  
  1568. PROCEDURE TDeleteCardCommand.Commit; OVERRIDE;
  1569.  
  1570.     BEGIN
  1571.     fCardDocument.fCache.Delete(fCard);
  1572.     fCardDocument.fCards.Delete(fCard);
  1573.     FreeIfObject(fCard);
  1574.     fCardDocument.fCardDocData.theCardCount := fCardDocument.fCards.GetSize;
  1575.     IF fCardDocument.fCardDocData.theShownCard > fCardNumber THEN
  1576.         BEGIN
  1577.         WITH fCardDocument.fCardDocData DO
  1578.             theShownCard := theShownCard - 1;
  1579.         WITH fCardDocument.fCardView DO
  1580.             fCurrNumber := fCurrNumber - 1;
  1581.         END;
  1582.     END;
  1583.  
  1584. {--------------------------------------------------------------------------------------------------}
  1585. {$S AFields}
  1586.  
  1587. PROCEDURE TDeleteCardCommand.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1588.                                                         fieldType: INTEGER)); OVERRIDE;
  1589.  
  1590.     BEGIN
  1591.     DoToField('TDeleteCardCommand', NIL, bClass);
  1592.     DoToField('fCardDocument', @fCardDocument, bObject);
  1593.     DoToField('fCardNumber', @fCardNumber, bInteger);
  1594.     DoToField('fCard', @fCard, bObject);
  1595.     INHERITED Fields(DoToField);
  1596.     END;
  1597.  
  1598. {--------------------------------------------------------------------------------------------------}
  1599. {$S ARes}
  1600.  
  1601. PROCEDURE TCardCache.ICardCache(size, growth: INTEGER);
  1602.  
  1603.     BEGIN
  1604.     IList;                                                { Initialize the list}
  1605.     fMaxSize := size;
  1606.     fGrowthRate := growth;
  1607.     fGeneration := 0;
  1608.     END;
  1609.  
  1610. {--------------------------------------------------------------------------------------------------}
  1611. {$S ARes}
  1612.  
  1613. PROCEDURE TCardCache.CacheCard(aCardDocument: TCardDocument; aCard: TCard);
  1614.  { Force the specified card to be in the cache, so that its data handle is
  1615.    valid.  There are a variety of conditions that can occur which makes caching
  1616.    the card difficult.    First, the cache could be full. If this is the case, we
  1617.    try to purge a card in the cache.  If no card can be purged (because they are
  1618.    locked) then we try to grow the cache (which in our implementation is always
  1619.    successful).
  1620.  
  1621.    If the card is already in the cache then we touch the card to tell the system
  1622.    that it had recently been accessed.    If it must be added to the cache, we read
  1623.    its contents from the disk and insert the card }
  1624.  
  1625.     LABEL 1000;
  1626.  
  1627.     VAR
  1628.         victimCard:         TCard;                        { Card to be paged out }
  1629.         fi:                 FailInfo;
  1630.  
  1631. {--------------------------------------------------------------------------------------------------}
  1632.  
  1633.     PROCEDURE HdlCacheFailed(error: OSErr; message: LongInt);
  1634.  
  1635.         BEGIN
  1636.         GOTO 1000;                                        { Pretend nothing happened }
  1637.         END;
  1638.  
  1639.     BEGIN
  1640.     CatchFailures(fi, HdlCacheFailed);
  1641.     IF CardIsInCache(aCard) THEN                        { already in memory }
  1642.     { Touch card as being recently accessed }
  1643.         Touch(aCard)
  1644.     ELSE                                                { not in memory }
  1645.         BEGIN
  1646.         WHILE fSize >= fMaxSize DO
  1647.             BEGIN
  1648.             victimCard := PurgeableCard(aCardDocument);
  1649.             IF victimCard = NIL THEN
  1650.                 Grow                                    { Grow the Cache if we can - Error if we
  1651.                                                          can't }
  1652.             ELSE
  1653.                 Delete(victimCard)                        { victim goes out }
  1654.             END;
  1655.   { ELSE cache not yet filled, so can blithely add it to the cache without
  1656.    first deleting something }
  1657.         aCard.fCardDocument.ReadCardFromDisk(aCard);
  1658.         Insert(aCard);
  1659.         END;
  1660.     Success(fi);
  1661. 1000: ;
  1662.     END;
  1663.  
  1664. {--------------------------------------------------------------------------------------------------}
  1665. {$S ARes}
  1666.  
  1667. FUNCTION TCardCache.CardIsInCache(aCard: TCard): BOOLEAN;
  1668. { Checks to see if aCard is already in the Cache}
  1669.  
  1670. {--------------------------------------------------------------------------------------------------}
  1671.  
  1672.     FUNCTION LookInCache(athing: TObject): BOOLEAN;
  1673.  
  1674.         BEGIN
  1675.         LookInCache := (TCard(athing) = aCard);
  1676.         END;
  1677.  
  1678.     BEGIN
  1679.     CardIsInCache := FirstThat(LookInCache) <> NIL;
  1680.     END;
  1681.  
  1682. {--------------------------------------------------------------------------------------------------}
  1683. {$S ARes}
  1684.  
  1685. PROCEDURE TCardCache.Delete(item: TObject); OVERRIDE;
  1686. { Deletes a card from the cache by flushing the card to disk and disposing of the
  1687.   handle to the card.  Set its data pointer to NIL to tell the system that it is
  1688.   no longer in memory}
  1689.  
  1690.     VAR
  1691.         aCard:                TCard;
  1692.  
  1693.     BEGIN
  1694.     aCard := TCard(item);
  1695.     aCard.fCardDocument.WriteCardToDisk(aCard);
  1696.     INHERITED Delete(aCard);
  1697.     DisposHandle(aCard.fData);
  1698.     aCard.fData := NIL;
  1699.     END;
  1700.  
  1701. {--------------------------------------------------------------------------------------------------}
  1702. {$S AFields}
  1703.  
  1704. PROCEDURE TCardCache.Fields(PROCEDURE DoToField(fieldName: Str255; fieldAddr: Ptr;
  1705.                                                 fieldType: INTEGER)); OVERRIDE;
  1706.  
  1707.     BEGIN
  1708.     DoToField('TCardCache', NIL, bClass);
  1709.     DoToField('fMaxSize', @fMaxSize, bInteger);
  1710.     DoToField('fGrowthRate', @fGrowthRate, bInteger);
  1711.     DoToField('fGeneration', @fGeneration, bInteger);
  1712.     INHERITED Fields(DoToField);
  1713.     END;
  1714.  
  1715. {--------------------------------------------------------------------------------------------------}
  1716. {$S AClose}
  1717.  
  1718. PROCEDURE TCardCache.FreeDocCards(aCardDocument: TCardDocument);
  1719. { Free all of the cards in the cache which belong to a document.}
  1720.  
  1721. {--------------------------------------------------------------------------------------------------}
  1722.  
  1723.     PROCEDURE MaybeDelete(item: TObject);
  1724.  
  1725.         BEGIN
  1726.         IF TCard(item).fCardDocument = aCardDocument THEN
  1727.             Delete(TCard(item));
  1728.         END;
  1729.  
  1730.     BEGIN
  1731.     Each(MaybeDelete);
  1732.     END;
  1733.  
  1734. {--------------------------------------------------------------------------------------------------}
  1735. {$S ANonRes}
  1736.  
  1737. PROCEDURE TCardCache.Grow;
  1738. { Grow the cache by the cache growth rate }
  1739.  
  1740.     BEGIN
  1741.     fMaxSize := fMaxSize + fGrowthRate;
  1742.     END;
  1743.  
  1744. {--------------------------------------------------------------------------------------------------}
  1745. {$S ARes}
  1746.  
  1747. PROCEDURE TCardCache.Insert(item: TObject);
  1748. { Insert a card in the cache and bump the generation count of the cache.  Attach
  1749.   this generation count to the card to let the purger no that this card has been recently
  1750.   accessed. }
  1751.  
  1752.     BEGIN
  1753.     Touch(TCard(item));
  1754.     INHERITED Insert(item);
  1755.     END;
  1756.  
  1757. {--------------------------------------------------------------------------------------------------}
  1758. {$S ARes}
  1759.  
  1760. PROCEDURE TCardCache.Touch(aCard: TCard);
  1761. { Bump the generation count }
  1762.  
  1763.     BEGIN
  1764.     fGeneration := fGeneration + 1;
  1765.     aCard.fGeneration := fGeneration;                    { Update generation of the card }
  1766.     END;
  1767.  
  1768. {--------------------------------------------------------------------------------------------------}
  1769. {$S ARes}
  1770.  
  1771. FUNCTION TCardCache.PurgeableCard(aCardDocument: TCardDocument): TCard;
  1772. { Determine which card to purge from the cache.  This is rather simplistic algorithm.  Find
  1773.   the oldest card that is not locked and does not belong to the currently active document.
  1774.   If all the cards belong to the currently active document then return the oldest of those.
  1775.   Finally, if all the cards are locked, return NIL.}
  1776.  
  1777.     VAR
  1778.         oldestInDoc:        TCard;
  1779.         oldestNotInDoc:     TCard;
  1780.         i:                    INTEGER;
  1781.         aCard:                TCard;
  1782.  
  1783.     BEGIN
  1784.     oldestInDoc := NIL;
  1785.     oldestNotInDoc := NIL;
  1786.     FOR i := 1 TO fSize DO
  1787.         BEGIN
  1788.         aCard := TCard(At(i));
  1789.         IF NOT aCard.fLocked THEN
  1790.             IF (aCard.fCardDocument = aCardDocument) THEN
  1791.                 BEGIN
  1792.                 IF (oldestInDoc <> NIL) THEN
  1793.                     BEGIN
  1794.                     IF aCard.fGeneration < oldestInDoc.fGeneration THEN
  1795.                         oldestInDoc := aCard;
  1796.                     END
  1797.                 ELSE
  1798.                     oldestInDoc := aCard;
  1799.                 END
  1800.             ELSE
  1801.                 BEGIN
  1802.                 IF (oldestNotInDoc <> NIL) THEN
  1803.                     BEGIN
  1804.                     IF aCard.fGeneration < oldestNotInDoc.fGeneration THEN
  1805.                         oldestNotInDoc := aCard;
  1806.                     END
  1807.                 ELSE
  1808.                     oldestNotInDoc := aCard;
  1809.                 END;
  1810.         END;
  1811.     IF oldestNotInDoc <> NIL THEN
  1812.         PurgeableCard := oldestNotInDoc
  1813.     ELSE
  1814.         PurgeableCard := oldestInDoc;
  1815.     END;
  1816.